#include <iostream>
#include <vector>
#include <exception>
#include <new>
#include <typeinfo>
#include <functional>

using namespace std;

template < class T >
void print(T t)
{
    cout << t << endl;
}

template<typename... Args> inline void pass(const Args&...) {}

template< typename ...Args >
inline void expand(Args... args)
{
    pass( (print(args), 1)... );
}

int f1(int i, int j) { return i+j; }
int f2(int i, int j) { return i-j; }

template < class ...Types>
void func( Types(* ...t)(int, int) )
{
    pass( (t(5, 2), 0)... );
}

template < class R, class... Args>
using FuncTmp = R (*)(Args...);

template < class ...Types >
void funcX( Types (*...ft)(Types, Types) )
{
    std::cout << "Size of pack: " << sizeof...(Types) << endl;

//    FuncTmp<int, int> ifnc;

    pass( ( print(ft(5, 2)), 0)... );
}

template < class T >
using ArrayTmp = std::initializer_list<T>;

template < class T >
void PrintArr( ArrayTmp<T> at )
{
    for (T val: at)
        print(val);
}

template < class ...Types >
void funcA(std::initializer_list<Types> ...arr)
{
    pass( (PrintArr(arr), 1)... );
}

template < class ...Types >
void sampleFunc(Types ...t)
{
    std::cout << "Size of parameter pack: " << sizeof...(Types) << endl;
    expand(t...);
}

template < class ...ClassTypes >
struct S
{
    template < class ...FuncTypes >
    void f(ClassTypes... ct, FuncTypes... ft)
    {
        expand( (ct + ft)... );
    }
};

template < class ...ClassTypes, class ...FuncTypes, class ...FuncTypes2 >
void f(ClassTypes... ct, FuncTypes... ft, FuncTypes2... ft2)
{
    expand( ft2... );
    std::cout << std::endl << std::endl;
    expand( ft... );
    std::cout << std::endl << std::endl;
    expand( ct... );
}

void printX( initializer_list<string> il )
{
    for (string cc: il)
        cout << cc << endl;
}

template < class... Types >
void expandTypes()
{
    printX( {typeid(Types).name()...} );
}

template < class T1, class T2 = void >
bool canThrow()
{
    return typeid(T1).before(typeid(T2)) || typeid(T1) == typeid(T2);
};

template < class... Types >
void mythrow() throw ( Types... )
{
    throw 1;

    if ( canThrow< std::bad_cast, Types...>() )
        throw std::bad_cast();
    else
        throw std::bad_exception();
}

template <class...> class EX{};

template < class... T >
struct X
{
    EX< T... > ex;

    void print(T... t)
    {
        expand(t...);
    };

};

template < int... I > struct TI {};

template <class> struct EX7;

template < template< class ... > class C, class ...T  >
struct EX7< C< T... > >
{
    C<T...> ct;

    void dosmth()
    {
        expandTypes<T...>();
        ct.print(11, 12);
    }
};

constexpr int fint()
{
    return 1;
}

int main()
{

    funcX(f1, f2);
    EX7< X<int, int> > ex7;
    ex7.dosmth();

    X<int, int> x;
    x.print(1, 2);

    terminate();

    try
    {
        mythrow<int>();
    }
    catch (std::exception)
    {
        std::cout << "OK";
    }
    catch(...)
    {
        std::cout << "Bang!!!";
    }

    func<int>


//    funcA( std::vector<int>({0, 1, 2, 3, 4, 5}), std::vector<char>( {'a', 'b', 'c'}) );
//    funcA( {0, 1, 2, 3, 4, 5}, {'a', 'b', 'c'}, {1.123} );

//    funcX(f1, f2);
//    sampleFunc(1, 'a', 0.0, "abcd");
//    xxx(1, 2);

//    S<int, int> s;
//    s.f(1, 2, 1.1, 2.1);
    return 0;
}
